package org.pblue.tumblarity.data.bean;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.Vector;

import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import org.pblue.appengine.AppengineDataException;
import org.pblue.appengine.AppengineDataManager;
import org.pblue.appengine.Cache;
import org.pblue.appengine.CacheList;
import org.pblue.appengine.DataBean;

@PersistenceCapable(identityType=IdentityType.APPLICATION, detachable="true")
public class Tumblelog extends DataBean {

  private static final long serialVersionUID = 120127666899124556L;
  
  public static final int FETCH_STATE_IDLE = 0;
  public static final int FETCH_STATE_PROCESSING = 1;
  public static final int FETCH_STATE_ERROR = 2;
  public static final int MAX_FETCH_RETRY = 60 * 24 * 7;
  public static final String AUTHENTICATION_COOKIE_NAME_PREFIX = "tumblarityorgauth_";
  public static final int AUTHENTICATION_COOKIE_AGE = 60 * 60 * 24 * 365;
  
  @Persistent
  private String email;
  
  @Persistent
  private String password;
  
  @Persistent
  private String tumblelog;
  
  @Persistent
  private String authenticationHash;
  
  @Persistent
  private Set<String> followerIds; 
  
  @Persistent
  private List<String> eventIds;
  
  @Persistent
  private int fetchState;
  
  @Persistent
  private int pageCount;
  
  @Persistent
  private Vector<Integer> pageHtmlHash;
  
  @Persistent
  private Set<String> fetchedFollowerIds;
  
  @Persistent
  private int fetchRetryCount;
  
  @Persistent 
  private Date lastFetched;
  
  public Tumblelog(String pEmail, String pTumblelog, String pPassword) {
    super(Tumblelog.generateId(pEmail, pTumblelog));
    
    this.setEmail(pEmail);
    this.setTumblelog(pTumblelog);
    this.setPassword(pPassword);
    this.setFollowerIds(new HashSet<String>());
    this.setEventIds(new ArrayList<String>());
    this.setFetchState(FETCH_STATE_IDLE);
    this.setPageCount(1);
    this.setFetchedFollowerIds(new HashSet<String>());
    this.setFetchRetryCount(0);
    
    Vector<Integer> pageHtmlHash = new Vector<Integer>();
    pageHtmlHash.add("".hashCode());
    this.setPageHtmlHash(pageHtmlHash);
  }
  
  public static String generateId(String pEmail, String pTumblelog) {
    return pEmail + (pTumblelog == null ? "" : pTumblelog);
  }
  
  public static String generateAuthenticationHash(String pPassword) {
    Random lengthRandom = new Random();
    int length = lengthRandom.nextInt(30) + 90;
    
    Random hashRandom = new Random();
    if (pPassword != null && !pPassword.equals("")) {
      hashRandom = new Random(pPassword.hashCode());
    }
    
    StringBuilder sb = new StringBuilder();
    
    for (int i = 0; i <= length; i++) {
      sb.append((char) (hashRandom.nextInt(25) + 97));
    }
    
    return sb.toString();
  }
  
  private String getAuthenticationCookieName() {
    return AUTHENTICATION_COOKIE_NAME_PREFIX + getId();
  }
  
  public Cookie getAuthenticationCookie() {
    Cookie cookie = new Cookie(getAuthenticationCookieName(), getAuthenticationHash());
    cookie.setPath("/");
    cookie.setMaxAge(Tumblelog.AUTHENTICATION_COOKIE_AGE);
    return cookie;
  }
  
  public boolean authenticateRequest(HttpServletRequest pRequest) {
    Cookie[] cookies = pRequest.getCookies();
    
    for (Cookie cookie : cookies) {
      if (cookie.getName().equals(getAuthenticationCookieName())) {
        String hash = cookie.getValue();
        if (this.getAuthenticationHash() != null && hash != null && hash.equals(this.getAuthenticationHash())) {
          return true;
        }
      }
    }
    
    return false;
  }
  
  public static Tumblelog get(String pEmail, String pTumblelog) throws AppengineDataException {
    String id = Tumblelog.generateId(pEmail, pTumblelog);
    return Tumblelog.get(id);
  }
  
  public static Tumblelog get(String pId) throws AppengineDataException {
    return (Tumblelog) DataBean.get(Tumblelog.class, pId, null);
  }
  
  public static Tumblelog get(String pEmail, String pTumblelog, AppengineDataManager pDataManager) throws AppengineDataException {
    String id = Tumblelog.generateId(pEmail, pTumblelog);
    return Tumblelog.get(id, pDataManager);
  }
  
  public static Tumblelog get(String pId, AppengineDataManager pDataManager) throws AppengineDataException {
    return (Tumblelog) DataBean.get(Tumblelog.class, pId, null, pDataManager);
  }
  
  public static final String EVENT_LIST_PREFIX = "tumblelog_events_";
  
  public CacheList<Event> getEvents() throws AppengineDataException {
    AppengineDataManager dataManager = AppengineDataManager.open();
    CacheList<Event> events;
    try {
      events = getEvents(dataManager);
    } finally {
      dataManager.close();
    }
    return events;
  }
  
  @SuppressWarnings("unchecked")
  public CacheList<Event> getEvents(AppengineDataManager pDataManager) throws AppengineDataException {
    String cacheKey = EVENT_LIST_PREFIX + getId();
    CacheList<Event> events = (CacheList<Event>) Cache.get(cacheKey);
    
    if (events == null) {
      try {
        events = new CacheList<Event>();
        events.setCacheKey(cacheKey);
        events.setDependencies(new HashSet<Object>());

        for (String eventId : getEventIds()) {
          Event event = Event.get(eventId, this, pDataManager);
          event.addDependency(events);
          events.add(event);
        }
        
        Cache.add(events);
      } catch (Exception e) {
        throw new AppengineDataException("Unable to retrieve events of tumblelog with id " + getId(), e);
      }
    }
    
    return events;
  }
  
  public static final String FOLLOWER_LIST_PREFIX = "tumblelog_followers_";
  
  public CacheList<Follower> getFollowers() throws AppengineDataException {
    AppengineDataManager dataManager = AppengineDataManager.open();
    CacheList<Follower> followers;
    try {
      followers = getFollowers(dataManager);
    } finally {
      dataManager.close();
    }
    return followers;
  }
  
  @SuppressWarnings("unchecked")
  public CacheList<Follower> getFollowers(AppengineDataManager pDataManager) throws AppengineDataException {
    String cacheKey = FOLLOWER_LIST_PREFIX + getId();
    CacheList<Follower> followers = (CacheList<Follower>) Cache.get(cacheKey);
    
    if (followers == null) {
      try {
        followers = new CacheList<Follower>();
        followers.setCacheKey(cacheKey);
        followers.setDependencies(new HashSet<Object>());

        for (String followerId : getFollowerIds()) {
          Follower follower = Follower.get(followerId, this, pDataManager);
          follower.addDependency(followers);
          followers.add(follower);
        }
        
        Cache.add(followers);
      } catch (Exception e) {
        throw new AppengineDataException("Unable to retrieve followers of tumblelog with id " + getId(), e);
      }
    }
    
    return followers;
  }
  
  public static final String FETCHED_FOLLOWER_LIST_PREFIX = "tumblelog_fetched_followers_";
  
  public CacheList<Follower> getFetchedFollowers() throws AppengineDataException {
    AppengineDataManager dataManager = AppengineDataManager.open();
    CacheList<Follower> fetchedFollowers;
    try {
      fetchedFollowers = getFetchedFollowers(dataManager);
    } finally {
      dataManager.close();
    }
    return fetchedFollowers;
  }
  
  @SuppressWarnings("unchecked")
  public CacheList<Follower> getFetchedFollowers(AppengineDataManager pDataManager) throws AppengineDataException {
    String cacheKey = FETCHED_FOLLOWER_LIST_PREFIX + getId();
    CacheList<Follower> fetchedFollowers = (CacheList<Follower>) Cache.get(cacheKey);
    
    if (fetchedFollowers == null) {
      try {
        fetchedFollowers = new CacheList<Follower>();
        fetchedFollowers.setCacheKey(cacheKey);
        fetchedFollowers.setDependencies(new HashSet<Object>());

        for (String fetchedFollowerId : getFetchedFollowerIds()) {
          Follower fetchedFollower = Follower.get(fetchedFollowerId, this, pDataManager);
          fetchedFollower.addDependency(fetchedFollowers);
          fetchedFollowers.add(fetchedFollower);
        }
        
        Cache.add(fetchedFollowers);
      } catch (Exception e) {
        throw new AppengineDataException("Unable to retrieve fetched followers of tumblelog with id " + getId(), e);
      }
    }
    
    return fetchedFollowers;
  }
  
  public static final String FETCHED_FOLLOWER_LIST_FOR_PAGE_PREFIX = "tumblelog_fetched_followers_for_page_";
  
  public CacheList<String> getFetchedFollowerIdsForPage(int pPage) throws AppengineDataException {
    AppengineDataManager dataManager = AppengineDataManager.open();
    CacheList<String> fetchedFollowerIds;
    try {
      fetchedFollowerIds = getFetchedFollowerIdsForPage(pPage, dataManager);
    } finally {
      dataManager.close();
    }
    return fetchedFollowerIds;
  }
  
  @SuppressWarnings("unchecked")
  public CacheList<String> getFetchedFollowerIdsForPage(int pPage, AppengineDataManager pDataManager) throws AppengineDataException {
    String cacheKey = FETCHED_FOLLOWER_LIST_FOR_PAGE_PREFIX + getId() + "_" + pPage;
    CacheList<String> fetchedFollowerIds = (CacheList<String>) Cache.get(cacheKey);
    
    CacheList<Follower> fetchedFollowers = getFetchedFollowers(pDataManager);
    
    if (fetchedFollowerIds == null && fetchedFollowers != null) {
      fetchedFollowerIds = new CacheList<String>();
      fetchedFollowerIds.setCacheKey(cacheKey);
      fetchedFollowerIds.setDependencies(new HashSet<Object>());
      
      for (Follower fetchedFollower : fetchedFollowers) {
        if (fetchedFollower.getPage() == pPage) {
          fetchedFollower.addDependency(fetchedFollowerIds);
          fetchedFollowerIds.add(fetchedFollower.getId());
        }
      }
      
      if (fetchedFollowerIds.size() > 0) {
        Cache.add(fetchedFollowerIds);
      }
    }
    
    return fetchedFollowerIds;
  }
  
  public String getEmail() {
    return email;
  }
  
  public void setEmail(String pEmail) {
    email = pEmail;
  }
  
  public String getTumblelog() {
    return tumblelog;
  }
  
  public void setTumblelog(String pTumblelog) {
    tumblelog = pTumblelog;
  }  
  
  
  public String getAuthenticationHash() {
    return authenticationHash;
  }

  
  public void setAuthenticationHash(String pAuthenticationHash) {
    authenticationHash = pAuthenticationHash;
  }

  public String getPassword() {
    return password;
  }
  
  public void setPassword(String pPassword) {
    password = pPassword;
  }

  public int getFetchState() {
    return fetchState;
  }
  
  public void setFetchState(int pFetchState) {
    fetchState = pFetchState;
  }
  
  public int getPageCount() {
    return pageCount;
  }
  
  public void setPageCount(int pPageCount) {
    pageCount = pPageCount;
  }
  
  
  public Vector<Integer> getPageHtmlHash() {
    return pageHtmlHash;
  }

  
  public void setPageHtmlHash(Vector<Integer> pPageHtmlHash) {
    pageHtmlHash = pPageHtmlHash;
  }

  public int getFetchRetryCount() {
    return fetchRetryCount;
  }

  
  public void setFetchRetryCount(int pFetchRetryCount) {
    fetchRetryCount = pFetchRetryCount;
  }
  
  public Date getLastFetched() {
    return lastFetched;
  }
  
  public void setLastFetched(Date pLastFetched) {
    lastFetched = pLastFetched;
  }

  
  public Set<String> getFollowerIds() {
    return followerIds;
  }

  
  public void setFollowerIds(Set<String> pFollowerIds) {
    followerIds = pFollowerIds;
  }

  
  public List<String> getEventIds() {
    return eventIds;
  }

  
  public void setEventIds(List<String> pEventids) {
    eventIds = pEventids;
  }

  
  public Set<String> getFetchedFollowerIds() {
    return fetchedFollowerIds;
  }

  
  public void setFetchedFollowerIds(Set<String> pFetchedFollowerIds) {
    fetchedFollowerIds = pFetchedFollowerIds;
  }
  
}
